package com.jsjvm.client.classparser.jconstants;

import com.jsjvm.client.classparser.JConstantPool;
import com.jsjvm.client.util.DataInputStream;

/** 
 * Represents constants of base types stored in class files.
 */
public class JBaseConstant implements JConstant {
	private Object value;
	private int size = 1;
	private int tag;

	public JBaseConstant(int tag, DataInputStream is) {
		this.tag = tag;
		switch(tag){
		case CONSTANT_Integer:
			this.value = is.readUInt();
			break;
		case CONSTANT_Float:
			this.value = readFloat(is);
			break;
		case CONSTANT_Long:
			this.value = is.readLong();
			this.size = 2;
			break;
		case CONSTANT_Double:
			this.value = readDouble(is);
			this.size = 2;
			break;
		case CONSTANT_String:
			// do nothing
		}
	}
	
	protected JBaseConstant(){
	}
	
	private static Float readFloat(DataInputStream is){
		Float value;
		int ivalue = is.readUInt();
		if (ivalue == 0x7f800000) {
			value = Float.POSITIVE_INFINITY;
		} else if (ivalue == 0xff800000) {
			value = Float.NEGATIVE_INFINITY;
		} else if ((ivalue >= 0x7f800001 && ivalue <= 0x7fffffff)
				|| (ivalue >= 0xff800001 && ivalue <= 0xffffffff)) {
			value = Float.NaN;
		} else {
			int s = ((ivalue >> 31) == 0) ? 1 : -1;
			int e = ((ivalue >> 23) & 0xff);
			int m = (e == 0) ? (ivalue & 0x7fffff) << 1
					: (ivalue & 0x7fffff) | 0x800000;

			value = (float) (s * m * Math.pow(2, e - 150));
		}
		return value;
	}
	
	private static Double readDouble(DataInputStream is){
		Double value;
		long lvalue = is.readLong();
		if (lvalue == 0x7f800000) {
			value = Double.POSITIVE_INFINITY;
		} else if (lvalue == 0xff800000) {
			value = Double.NEGATIVE_INFINITY;
		} else if ((lvalue >= 0x7f800001 && lvalue <= 0x7fffffff)
				|| (lvalue >= 0xff800001 && lvalue <= 0xffffffff)) {
			value = Double.NaN;
		} else {
			int s = ((lvalue >> 63) == 0) ? 1 : -1;
			int e = (int) ((lvalue >> 52) & 0x7ffL);
			long m = (e == 0) ? (lvalue & 0xfffffffffffffL) << 1
					: (lvalue & 0xfffffffffffffL) | 0x10000000000000L;

			value = (double) (s * m * Math.pow(2, e - 1075));
		}
		return value;
	}

	@Override
	public JConstant link(JConstantPool cpInfo) {
		return this;
	}
	
	public int getSize() {
		return size;
	}
	
	public Object getValue() {
		return value;
	}

	// only subclasses are allowed to set the value
	protected void setValue(Object value) {
		this.value = value;
	}
	
	public int getTag() {
		return tag;
	}
}
